<?php

	define('VERSION', 0.1);

	function dump($variable)
	{
		echo '<pre>';
		echo var_dump($variable);
		echo '</pre>';
	}

	class Loader
	{
		static $objects = array();
		static $database_connections = array();
		
		public static function &instance($key, $value = null)
		{
			if($value !== null)
			{
				self::$objects[$key] = $value;
			}
			
			return self::$objects[$key];
		}
		
		public static function &database($key)
		{
			if(!isset(self::$database_connections[$key]))
			{
				$settings = self::instance('controller')->config['database']['connections'][$key];
				
				switch($settings['provider'])
				{
					case 'mysql':
						self::$database_connections[$key] = new Mysql_Database($settings['host'], $settings['port'], $settings['name'], $settings['username'], $settings['password']);
						break;
				}
			}
			
			return self::$database_connections[$key];
		}
	}
	
	class Benchmark
	{
		static $markers = array();
		
		public static function mark($name)
		{
			$markers[$name] = microtime();
		}
		
		public static function elapsed($marker1, $marker2 = null)
		{
			if($marker2 === null)
			{
				$marker2 = microtime();
			}
			else
			{
				$marker2 =& self::$markers[$marker2];
			}
			
			$marker1 =& self::$markers[$marker1];
		}
	}
	
	class Application
	{
		private $config;
		private $route;
		private $controller;
		
		public function __construct($config)
		{
			$this->config =& $config;
		}
		
		public function run()
		{
			$this->parse_url();
			$this->load_controller_object();
			$this->load_plugins();
			$this->run_action();
		}
		
		private function parse_url()
		{
			$this->route['controller'] = $this->config['routing']['default_controller'];
			$this->route['action'] = $this->config['routing']['default_action'];
			$this->route['parameters'] = array();
			
			if(isset($_SERVER['PATH_INFO']))
			{
				$url_segments = array_filter(explode('/', $_SERVER['PATH_INFO']));
				
				if(isset($url_segments[1]) && !empty($url_segments[1]))
				{
					$this->route['controller'] = $url_segments[1];
					array_shift($url_segments);
					
					if(isset($url_segments[0]) && !empty($url_segments[0]))
					{
						$this->route['action'] = $url_segments[0];
						array_shift($url_segments);
						$this->route['parameters'] = $url_segments;
					}
				}
			}
		}
		
		private function load_controller_object()
		{
			$file = APP_DIR . 'controllers' . DS . $this->route['controller'] . '.php';
			
			if(file_exists($file))
			{
				include $file;
				
				$this->controller =& Loader::instance('controller', new $this->route['controller']());
				$this->controller->config =& $this->config;
				$this->controller->route =& $this->route;
			}
			else
			{
				exit('could not include controller: ' . $file);
			}
		}
		
		private function load_plugins()
		{
			$this->controller->plugins = new Plugins();
			
			foreach($this->config['system_plugins'] as $system_plugin)
			{
				$this->controller->plugins->load($system_plugin);

				$pre_action = $this->controller->plugins->$system_plugin->get_pre_action();
				
				if($pre_action !== false)
				{
					$this->controller->plugins->$system_plugin->$pre_action();
				}
			}
		}
		
		private function run_action()
		{
			if(method_exists($this->controller, $this->route['action']))
			{
				if(count($this->route['parameters']) === 0)
				{
					call_user_func(array($this->controller, $this->route['action']));
				}
				else
				{
					call_user_func_array(array($this->controller, $this->route['action']), $this->route['parameters']);
				}
			}
			else
			{
				exit('could not call action: ' . $this->route['action']);
			}
		}
	}
	
	class Plugins
	{
		private $plugins = array();
		
		public function &__get($key)
		{
			return $this->plugins[$key];
		}
		
		public function load($name, $folder = false)
		{
			$file = SYS_DIR . 'plugins' . DS . $name . '.php';
			
			if($folder !== false)
			{
				$file = SYS_DIR . 'plugins' . DS . $folder . DS . $name . '.php';
			}

			if(file_exists($file))
			{
				include $file;
				
				if(class_exists($name))
				{
					$this->plugins[$name] = new $name();
				}
			}
			else
			{
				exit('the plugin: ' . $name . ' did not exist');
			}
		}
	}
	
	class Plugin
	{
		private $pre_action = false;
		protected $controller;
		
		public function __construct()
		{
			$this->controller =& Loader::instance('controller');
		}
		
		public function get_pre_action()
		{
			return $this->pre_action;
		}
		 
		public function set_pre_action($action_name = 'load')
		{
			$this->pre_action = $action_name;
		}
	}
	
	class Models
	{
		private $models = array();
		
		public function &__get($key)
		{
			return $this->models[$key];
		}
		
		public function load($name, $folder = false)
		{
			$file = APP_DIR . 'models' . DS . $name . '.php';
			
			if($folder !== false)
			{
				$file = APP_DIR . 'models' . DS . $folder . DS . $name . '.php';
			}

			if(file_exists($file))
			{
				include $file;
				
				if(class_exists($name))
				{
					$this->models[$name] = new $name();
				}
			}
			else
			{
				exit('the model: ' . $name . ' did not exist');
			}
		}
	}
	
	class View
	{
		private $data = array();
		private $file;
		
		public function __construct($file)
		{
			$this->file = APP_DIR . 'views' . DS . $file;
		}
		
		public function &__get($key)
		{
			return $this->data[$key];
		}
		
		public function __set($key, $value)
		{
			$this->data[$key] = $value;
		}
		
		public function display()
		{
			foreach($this->data as $key => &$value)
			{
				$$key = $value;
			}
			
			include $this->file;
		}
	}

	class Mysql_Database extends Pdo
	{
		public function __construct($host, $port, $name, $username, $password)
		{
			parent::__construct('mysql:host=' . $host . ';port=' . $port . 'dbname=' . $name, $username, $password); 
		}
		
		public function query($query, $bindings = null)
		{
			$stmt = $this->prepare($query);
			
			if($bindings !== null)
			{
				if(!is_array($bindings))
				{
					$bindings = is_array($bindings);
				}
				
				$stmt->execute($bindings);
			}
			else
			{
				$stmt->execute();
			}
			
			$result['query'] = $query;
			$result['bindings'] = $bindings;			
			$result['error'] = $stmt->errorInfo();
			$result['result'] = $stmt->fetchAll();

			if(isset($result['error'][2]) && !empty($result['error'][2]))
			{
				exit(var_dump($result));
			}
			
			return $result;
		}
	}
	
?>